﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace Server
{
    /// <summary>
    /// This class manages a collection of users logged into our server.
    /// </summary>
    [XmlRoot ("UserManager")]
    [XmlInclude(typeof(User))]
    public class UserManager
    {
        /// <summary>
        /// A quick way to look up a user by their user id.  Key=user id, Value=user class for that id
        /// </summary>
        private Dictionary<string, User> users;

        /// <summary>
        /// Note that we cannot serialize a generic dictionary.
        /// </summary>
        [XmlIgnore]
        public Dictionary<string, User> Users
        {
            get { return users; }
            set { users = value; }
        }

        /// <summary>
        /// Property created to fool xml serialization
        /// </summary>
        [XmlArray("Users")] 
        [XmlArrayItem("UsersLine", Type=typeof(DictionaryEntry))]
        public DictionaryEntry[] UserArray
        {
            get
            {
                //Make an array of DictionaryEntries to return 
                DictionaryEntry[] ret = new DictionaryEntry[Users.Count];
                int i = 0;
                DictionaryEntry de;
                //Iterate through Stuff to load items into the array. 
                foreach (KeyValuePair<string, User> usersLine in Users)
                {
                    de = new DictionaryEntry();
                    de.Key = usersLine.Key;
                    de.Value = usersLine.Value;
                    ret[i] = de;
                    i++;
                }
                return ret;
            }
            set
            {
                Users.Clear();
                for (int i = 0; i < value.Length; i++)
                {
                    Users.Add((string)value[i].Key, (User)value[i].Value);
                }
            }
        }
        /// <summary>
        /// Allowable results for a login
        /// </summary>
        public enum LoginResults { Ok, NoSuchUser, BadPassword };

        /// <summary>
        /// Constructor, makes a new instance of class
        /// </summary>
        public UserManager()
        {
            users = new Dictionary<string, User>();
        }
        /// <summary>
        /// Given a user id and password, add a new user to the collection of our users
        /// Returns a reference to the user created in the out parameter.
        /// </summary>
        /// <param name="name">The user id of the new user</param>
        /// <param name="pass">The password of the new user</param>
        /// <returns>true if that user name does not already exist, false otherwise</returns>
        public bool AddUser(string name, string pass, ClientHandler connection, out User user)
        {
           
            if (FindUser(name) == null)
            {
                user = new User(name, pass, connection);
                users.Add(name, user);
                System.Console.WriteLine("Adding user {0} ", name);

                // serialize the user list
                Serialize();
                return true;
            }
            user = null;
            System.Console.WriteLine("User {0} attempted dual registration -- ignored", name);
            return false;
        }
        /// <summary>
        /// Look up a user id and find it in the dictionary
        /// </summary>
        /// <param name="name">The user id to look up</param>
        /// <returns>the user instance for that user if found, null otherwise</returns>
        public User FindUser(string name)
        {
            System.Console.WriteLine("Looking up " + name);
            if (users.ContainsKey(name))
                return users[name];
            else
                return null;
        }
        /// <summary>
        /// Try to login a user.  Lookup the user id.  If not found, user doesn't exist.  If found, check the
        /// password to ensure a match.
        /// </summary>
        /// <param name="name">The user id</param>
        /// <param name="pass">The password</param>
        /// <returns>NoSuchUser if user not found, Ok if good password, BadPassword if bad password</returns>
        public LoginResults Authenticate(string name, string pass)
        {
            Console.WriteLine("Authenticating user: " + name);
            User usr = FindUser(name);

            if (usr == null)
                return LoginResults.NoSuchUser;
            else if (usr.Authenticate(pass))
                return LoginResults.Ok;
            else
                return LoginResults.BadPassword;
        }
        /// <summary>
        /// Send a message to all connected users
        /// </summary>
        /// <param name="p"></param>
        internal void SendToAll(string message, User sender)
        {
            System.Console.WriteLine("Sending message {0} from user {1}", message, sender.Name);
            foreach (User u in users.Values)
            {
                if (u != sender)
                {
                    if (u.IsConnected)
                        u.SendMessage(message);
                }
            }
        }

        /// <summary>
        /// Serialize the list of registered users. This method runs every time a new user is added.
        /// </summary>
        public void Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(UserManager));
            string filename = "users.xml";
            StreamWriter outStream = new StreamWriter(filename, false);
            serializer.Serialize(outStream, this);
            outStream.Close();
        }
    }
}
